#include "msg.h"

namespace msg
{
	int __step_nb=0;
	bool __numbering_paused=false;
	string last_section;
	
	string boolToYesNo(bool value)
	{
		return value ? "yes" : "no";
	}
		
	//see msg.h for default parameter values
	void Info(string txt, char borderStartAndEndAndBackground, char background,char borderEndOverride,int msgSize, bool autoCenter)
	{
		int l = txt.length();
		if(l>0)
		{
			txt = " "+txt+" ";
			l+=2;
		}
		borderEndOverride = (borderEndOverride != 'X' ? borderEndOverride : borderStartAndEndAndBackground);
		background = (background != 'X' ? background : borderStartAndEndAndBackground);
		cout << borderStartAndEndAndBackground;
		if(!autoCenter)
			cout <<"\t"<<txt;
		else
			cout << setfill(background) << setw((msgSize-2-l)/2+l) << txt << setw((msgSize-2-l)/2+(msgSize-2-l)%2) << "";
		cout << borderEndOverride << endl;
	}
	
	void ClearScreen()
	{
		//note : see http://www.yolinux.com/TUTORIALS/LinuxTutorialC++.html and look for "sparc"
		//see also : http://svn.python.org/projects/python/branches/release25-maint/PC/pyconfig.h
		//http://lists.apple.com/archives/darwin-development/2002/Feb/msg00605.html for mac
		#if defined WIN32 || defined __CYGWIN32 || defined MS_WINDOWS
		system("cls");
		#elif defined(linux) || defined(sparc) || defined(sgi) || defined(__gnu_linux__) || defined(__ppc__) || defined (__i386__)
		system("clear");
		#else
		system("cls"); //if nothing is defined, we assume we're on 
		#endif
	}
	
	void Pause(bool clearScreen,string otherMessage)
	{
		if(clearScreen)
			cout << endl << otherMessage;
		else
			cout << "----";
// 		getchar();
		string tmp;
		//flush buffer if not empty
		if(!cin.eof())
			getline(cin,tmp);
		if(clearScreen)
		{
			ClearScreen();
		}
	}
	
	void BigTitle(string txt)
	{
		Info("",'-');
		Info("",'|',' ');
		Info(txt,'|',' ');
		Info("",'|',' ');
		Info("",'-');
		cout << endl;
	}
	
	void Section(string txt)
	{
		last_section = txt;
		Info(txt,'=');
		Pause(true,"Press enter to start testing");
	}
	
	void SectionEnd()
	{
		Pause();
		Info(last_section,'=');
		cout << "---- Tests are finished for this section"<<endl;
	}
	
	void Underlined(string txt)
	{
		unsigned int l;
		cout << txt << endl; //print the text to underline first
		l = txt.find('\n'); //get the position of the first \n (the length of the first line used to determine the nb of - to use for underlining)
		if(l == std::string::npos) //if there actually wasn't any \n found
			l = txt.length(); //we use the normal string length to tell the number of - to print
		Info("",'_','X','X',l);
		cout << endl;
	}
	
	//see msg.h for default parameter values
	void Step(string txt,int restartPauseContinueNumbering)
	{
		switch(restartPauseContinueNumbering)
		{
			case 0:
				__step_nb = 1;
				__numbering_paused = false;
				break;
			case 1:
				__numbering_paused = true;
				break;
			case 2:
				__numbering_paused = false;
				break;
			case -1:
				break;
			default:
				cout << "Step(string "<<txt<<" int restartPauseContinueNumbering= "<<txt<<") unknown value for second parameter"<<endl;
		}
		
		if(__numbering_paused)
			Underlined(txt);
		else
		{
			ostringstream a;
			a << __step_nb;
			txt = "Step "+a.str()+". "+txt;
			Underlined(txt);
			__step_nb++;
		}
	}
	
	
	MenuItem::MenuItem(void (*testFctPtr) (),string description):testFctPtr(testFctPtr),description(description)
	{
	}
	
	Menu::Menu(string my_title):my_title(my_title)
	{}
			
	void Menu::add(MenuItem& i)
	{
		menuItems.push_back(i);
	}
			
	void Menu::run()
	{
		bool reloop=true;
		bool sayInputMistake = false;
		if(menuItems.size() > 0)
		{
			while(reloop)
			{
				ClearScreen();
				BigTitle(my_title);
				//first display the possible choices in an NB. DESCR\n list
				displayList(); //note: the last item of the menu has id:menuItems.size()+1 and is the quit option
				if(sayInputMistake)
				{
					cout << "incorrect choice, please try again"<<endl;
					sayInputMistake = false;
				}
				
				//ask the user to choose something and study what he did
				switch(getInput()) //getInput takes care of launching the functions corresponding to the user choices when those are correct
				{
					case 0:
						break; //that's cool user launched one of the tests
					case 1:
						sayInputMistake = true; //tell the user he made a mistake
						break;
					case 2:
						reloop = false;
						//user asked to quit
						break;
					default:
						cout << "Menu::run() switch(getInput()) shouldn't reach this point !"<<endl;
				}
			}
			//some credits
			ClearScreen();
			cout << "Thanks for using this software" << endl << "Hamza Shawy & Jonathan-david Schroder"<<endl;
		}
	}
	//description of items along with numbers for user to choose + a quit choice as a last item
	void Menu::displayList()
	{
		list<MenuItem>::iterator it;
		int count=0;
		string out;
		
		cout << "Choose a group of LinkedList's member functions you want to test"<<endl <<"by entering the corresponding number." << endl << endl;
		//printing all the items according to our list and the provided descriptions
		for(it=menuItems.begin();it!=menuItems.end();it++)
		{
			count++;
			ostringstream tmp;
			tmp << count << ". " << it->description;
			out = tmp.str();
			Info(out,' ',' ',' ',DEFAULTMSGSIZE ,false); //print with just a tab distance without centering text
		}
		//printing the last item : quitting
		ostringstream tmp;
		tmp << ++count << ". quit";
		out = tmp.str();
		Info(out,' ',' ',' ',DEFAULTMSGSIZE ,false); //print with just a tab distance without centering text
		//jump one line
		cout << endl;
	}
			
			//keeps asking for a valid choice among those proposed in the menu
			//return values :
			//0:keep going, because good input by user & did not ask for quit
			//1:keep going, but user made a mistake input : write that did not understand input
			//2:user asked to quit
	int Menu::getInput()
	{
		unsigned int inputVal;
		string tmp;
		int result;
		list<MenuItem>::iterator it;
		unsigned int a;
		istringstream* integer=NULL;
		cout << "Choice:" << endl;
		getline(cin,tmp);
		integer = new istringstream(tmp);
		if(*integer >> inputVal)
		{
			//cout << "could convert to integer:"<< a << endl;
			if(inputVal >= 1 && inputVal <= menuItems.size())
			{
				
				inputVal--; //set integer to start at 0
				it = menuItems.begin();
				for(a=0;a<inputVal;a++) //note: for loop not executed for inputVal=0 => it will really point to the first element
					it++;
				//clear the screen because the functions to call don't do it
				ClearScreen();
				//print the class testing big title & fct description
				BigTitle(my_title);
				Section(it->description);
				(*(it->testFctPtr))(); //calling the menu function
				SectionEnd();
				result = 0; //keep going everything went ok
			}
			else if(inputVal == menuItems.size()+1) //if user asked to quit
			{
				result = 2;//please quit
			}
			else //integer from user out of range
				//returns what's necessary so that we tell it to the user
				//on the next menu re-display
				result=1; //keep going & tell mistake in input
		}
		//if conversion to int did not work
		else
		{
	// 					cout << "conversion failed from:"<<tmp<<endl;
			result = 1; //keep going; user just made a mistake
		}
		return result;
	}
}
